package com.meisupic.copyright.service.impl;

import com.meisupic.copyright.domain.po.RealNameAuth;
import com.meisupic.copyright.exception.BizException;
import com.meisupic.copyright.service.ITimevaleSignService;
import com.meisupic.copyright.util.EncryptionHMAC;
import com.timevale.esign.sdk.tech.bean.OrganizeBean;
import com.timevale.esign.sdk.tech.bean.PersonBean;
import com.timevale.esign.sdk.tech.bean.result.AddAccountResult;
import com.timevale.esign.sdk.tech.bean.result.Result;
import com.timevale.esign.sdk.tech.bean.result.SignDataResult;
import com.timevale.esign.sdk.tech.impl.constants.LegalAreaType;
import com.timevale.esign.sdk.tech.impl.constants.OrganRegType;
import com.timevale.esign.sdk.tech.service.AccountService;
import com.timevale.esign.sdk.tech.service.EsignsdkService;
import com.timevale.esign.sdk.tech.service.SignService;
import com.timevale.esign.sdk.tech.service.factory.AccountServiceFactory;
import com.timevale.esign.sdk.tech.service.factory.EsignsdkServiceFactory;
import com.timevale.esign.sdk.tech.service.factory.SignServiceFactory;
import com.timevale.tech.sdk.bean.HttpConnectionConfig;
import com.timevale.tech.sdk.bean.ProjectConfig;
import com.timevale.tech.sdk.bean.SignatureConfig;
import com.timevale.tech.sdk.constants.AlgorithmType;
import com.timevale.tech.sdk.constants.HttpType;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * creation wuxun
 * @version V1.0
 * @Description: TODO
 * @date 2018/1/22 22:10
 */
@Service
public class TimevaleSignServiceImpl implements ITimevaleSignService {

  private static String encoding = "UTF-8";

  private static String algorithm = "HmacSHA256";

  @Value("${esign.projectId}")
  private String projectId;

  @Value("${esign.projectSecret}")
  private String projectSecret;

  @Value("${esign.itsmApiUrl}")
  private String itsmApiUrl;

  @Value("${esign.personAuth}")
  private String personAuthUrl;

  @Value("${esign.enterpriseAuth}")
  private String enterpriseAuthUrl;

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  /**
   * e签宝初始化
   */
  @PostConstruct
  public void init() {
    ProjectConfig proCfg = new ProjectConfig();

    // 项目ID(应用ID)
    proCfg.setProjectId(projectId);
    // 项目Secret(应用Secret)
    proCfg.setProjectSecret(projectSecret);
    // 开放平台地址
    proCfg.setItsmApiUrl(itsmApiUrl);

    HttpConnectionConfig httpConCfg = new HttpConnectionConfig();
    // 协议类型，默认https
    httpConCfg.setHttpType(HttpType.HTTPS);
    // 请求失败重试次数，默认5次
    httpConCfg.setRetry(5);
    // 代理服务IP地址
    // httpConCfg.setProxyIp(null);
    // 代理服务端口
    // httpConCfg.setProxyPort(0);

    SignatureConfig sCfg = new SignatureConfig();
    // 算法类型，默认HMAC-SHA256
    sCfg.setAlgorithm(AlgorithmType.HMACSHA256); // 可选RSA，但推荐使用HMACSHA256
    // e签宝公钥，可以从开放平台获取。若算法类型为RSA，此项必填
    sCfg.setEsignPublicKey("");
    // 平台私钥，可以从开放平台下载密钥生成工具生成。若算法类型为RSA，此项必填
    sCfg.setPrivateKey("");
    logger.info("--项目初始化...");
    EsignsdkService sdk = EsignsdkServiceFactory.instance();
    Result result = sdk.init(proCfg, httpConCfg, sCfg);
    if (0 != result.getErrCode()) {
      logger.info("--项目初始化失败：errCode=" + result.getErrCode() + " msg=" + result.getMsg());
    } else {
      logger.info("--项目初始化成功！errCode={},msg={}", result.getErrCode(), result.getMsg());
    }
  }

  /***
   * 创建个人账户 使用到接口：accountService.addAccount(organizeBean);
   */
  public String addPersonAccount(RealNameAuth realNameAuth) {
    PersonBean personBean = new PersonBean();
    personBean.setEmail(realNameAuth.getEmail());
    personBean.setMobile(realNameAuth.getMobile());
    personBean.setName(realNameAuth.getAuthName());
    personBean.setIdNo(realNameAuth.getDocumetNo());
    personBean.setPersonArea(LegalAreaType.MAINLAND);
    AccountService accountService = AccountServiceFactory.instance();
    AddAccountResult addAccountResult = accountService.addAccount(personBean);
    if (0 != addAccountResult.getErrCode()) {
      logger.info("创建个人账户失败，errCode=" + addAccountResult.getErrCode() + " msg=" + addAccountResult
          .getMsg());
      throw new BizException("创建个人账户失败,失败原因：" + addAccountResult.getMsg());
    } else {
      logger.info("创建个人账户成功！accountId = " + addAccountResult.getAccountId());
    }
    return addAccountResult.getAccountId();
  }

  /***
   * 创建企业账户,该企业账户是一个相对概念。可以理解成是你们公司的客户，这个客户是一个企业；
   * 使用到接口：accountService.addAccount(organizeBean);
   */
  public String addOrganizeAccount(RealNameAuth realNameAuth) {
    OrganizeBean organizeBean = new OrganizeBean();
    organizeBean.setEmail(realNameAuth.getEmail());
    // 企业名称
    organizeBean.setName(realNameAuth.getAuthName());
    // 单位类型，0-普通企业，1-社会团体，2-事业单位，3-民办非企业单位，4-党政及国家机构，默认0
    organizeBean.setOrganType(0);
    // 企业注册类型，NORMAL:组织机构代码号，MERGE：多证合一，传递社会信用代码号,REGCODE:企业工商注册码,默认NORMAL
    organizeBean.setRegType(getRegType(realNameAuth.getDocumentType()));
    // 组织机构代码号、社会信用代码号或工商注册号
    organizeBean.setOrganCode(realNameAuth.getDocumetNo());

    // 注册类型，1-代理人注册，2-法人注册，默认1
    organizeBean.setUserType(2);

    // 代理人姓名，当注册类型为1时必填
    //organizeBean.setAgentName("艾利");
    // 代理人身份证号，当注册类型为1时必填
    //organizeBean.setAgentIdNo("220301198705170035");

    // 法定代表姓名，当注册类型为2时必填
    organizeBean.setLegalName(realNameAuth.getLegalName());
    // 法定代表人归属地,0-大陆，1-香港，2-澳门，3-台湾，4-外籍，默认0
    organizeBean.setLegalArea(0);
    // 法定代表身份证号/护照号，当注册类型为2时必填
    organizeBean.setLegalIdNo(realNameAuth.getLegalIdentityCardNo());

    logger.info("----开始创建企业账户...");
    AccountService accountService = AccountServiceFactory.instance();
    AddAccountResult addAccountResult = accountService.addAccount(organizeBean);

    if (0 != addAccountResult.getErrCode()) {
      logger.info("创建企业账户失败，errCode={}msg={}", addAccountResult.getErrCode(), addAccountResult
          .getMsg());
      throw new BizException(addAccountResult.getMsg());
    } else {
      System.out.println("创建企业账户成功！accountId = " + addAccountResult.getAccountId());
      logger.info("创建企业账户成功！accountId = " + addAccountResult.getAccountId());
    }
    return addAccountResult.getAccountId();
  }

  @Override
  public String localTextSign(String srcData) {
    SignService signService = SignServiceFactory.instance();
    SignDataResult signDataResult = signService.localTextSign(srcData);
    if(0 != signDataResult.getErrCode()){
      throw new BizException(signDataResult.getMsg());
    }
    return signDataResult.getSignResult();
  }

  @Override
  public String localTextSign(String accountId, String srcData) {
    SignService signService = SignServiceFactory.instance();
    SignDataResult signDataResult = signService.localTextSign(accountId,srcData);
    if(0 != signDataResult.getErrCode()){
      throw new BizException(signDataResult.getMsg());
    }
    return signDataResult.getSignResult();
  }

  @Override
  public String personAUth(String personApply) {

    try {
      String signature = EncryptionHMAC
          .getHMACHexString(personApply, projectSecret, algorithm, encoding);
      return sendPost(personAuthUrl,signature,personApply);
    } catch (Exception e) {
      logger.error("个人实名认证签名错误{}",e.getMessage());
      throw new BizException("系统异常，请稍后重试");
    }
  }

  @Override
  public String enterpriseAuth(String enterpriseApply) {
    try {
      String signature = EncryptionHMAC
          .getHMACHexString(enterpriseApply, projectSecret, algorithm, encoding);
      return sendPost(enterpriseAuthUrl,signature,enterpriseApply);
    } catch (Exception e) {
      logger.error("企业实名认证签名错误{}",e.getMessage());
      throw new BizException("系统异常，请稍后重试");
    }
  }


  private OrganRegType getRegType(Integer dciDocType) {
    if (5 == dciDocType) {
      return OrganRegType.MERGE;
    }
    return OrganRegType.NORMAL;
  }

  /***
   * 设置Headers报头信息
   *
   * @param jsonStr
   * @return
   * @throws Exception
   */
  private Map<String, String> getHeaders(String signature, String jsonStr) throws Exception {
    Map<String, String> headersMap = new LinkedHashMap<String, String>();
    headersMap.put("X-timevale-project-id", projectId);
    headersMap.put("X-timevale-signature", signature); // 请求参数和projectSecret参数通过HmacSHA256加密的16进制字符串
    headersMap.put("signature-algorithm", algorithm);
    headersMap.put("Content-Type", "application/json");
    headersMap.put("Charset", encoding);
    return headersMap;
  }

  /***
   * 模拟发送POST请求
   *
   * @param jsonStr
   * @return
   * @throws Exception
   */
  public String sendPost(String urlPath, String signature, String jsonStr) throws Exception {
    String result = null;
    // 建立连接
    URL url = new URL(urlPath);
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    // 设置Headers参数
    httpConn.setDoOutput(true); // 需要输出
    httpConn.setDoInput(true); // 需要输入
    httpConn.setUseCaches(false); // 不允许缓存
    httpConn.setRequestMethod("POST"); // 设置POST方式连接
    // 设置Headers属性
    for (Entry<String, String> entry : getHeaders(signature, jsonStr).entrySet()) {
      httpConn.setRequestProperty(entry.getKey(), entry.getValue());
    }

    // 连接会话
    httpConn.connect();

    // 建立输入流，向指向的URL传入参数
    DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());
    // 设置请求参数
    dos.write(jsonStr.toString().getBytes("UTF-8"));
    System.out.println(jsonStr);
    dos.flush();
    dos.close();
    // 获得响应状态
    int resultCode = httpConn.getResponseCode();
    if (HttpURLConnection.HTTP_OK == resultCode) {
      StringBuffer sb = new StringBuffer();
      String readLine = new String();
      BufferedReader responseReader = new BufferedReader(
          new InputStreamReader(httpConn.getInputStream(), "UTF-8"));
      while ((readLine = responseReader.readLine()) != null) {
        sb.append(readLine);
      }
      // System.out.println(httpConn.get.getRequestURI());
      responseReader.close();
      result = sb.toString();
    }
    return result;
  }

}
